/* vim: set ts=2 et sw=2 cindent fo=qroca: */

package com.globant.google.mendoza;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/** Represents the validation of a merchant calculation.
 */
public final class MendozaMerchantCalculationValidation {

  /** True if the result size is the expected size. */
  private boolean correctSize = true;

  /** The expected size of the calculation result. */
  private int expectedResultSize = 0;

  /** The size of the calculation result. */
  private int resultSize = 0;

  /** The address ids that exist in the result but were not requested in the
   * callback. */
  private Set<String> notRequestedAddressIdSet = new HashSet<String>();

  /** The shipping methods that exist in the result but were not requested in
   * the callback. */
  private Set<String> notRequestedShippingNameSet = new HashSet<String>();

  /** The address ids that not exist in the result but were requested in the
   * callback. */
  private Set<String> missingRequestedAddressIdSet = new HashSet<String>();

  /** The shipping methods that not exist in the result but were requested in
   * the callback. */
  private Set<String> missingRequestedShippingNameSet = new HashSet<String>();

  /** True if the calculation timeout.
   * True if the calculation response time is higher than the expected.
   */
  private boolean calculationTimeout;

  /** The max merchant calculation response time.*/
  private long maxResponseTime;

  /** Constructor. Creates an instance of MendozaMerchantCalculationValidation.
   *
   * @param maxCalculationResponseTime The max merchant calculation
   * response time.
   */
  public MendozaMerchantCalculationValidation(
      final long maxCalculationResponseTime) {
    maxResponseTime = maxCalculationResponseTime;
  }

  /** Gets the size of the calculation result.
   *
   * @return Returns the size of the calculation result.
   */
  public int getResultSize() {
    return resultSize;
  }

  /** Gets the expected size of the calculation result.
   *
   * @return Returns the expected size of the calculation result.
   */
  public int getExpectedResultSize() {
    return expectedResultSize;
  }

  /** True if the result size is the expected size.
   *
   * @return Returns true if the result size is the expected size.
   */
  public boolean isCorrectSize() {
    return correctSize;
  }

  /** Gets the address ids that exist in the result but
   *  were not requested in the callback.
   *
   * @return Returns the not requested result address ids.
   */
  public Set<String> getNotRequestedAddressIdSet() {
    return notRequestedAddressIdSet;
  }

  /** The shipping methods that exist in the result but were
   * not requested in the callback.
   *
   * @return Returns the not requested result shipping methods.
   */
  public Set<String> getNotRequestedShippingNameSet() {
    return notRequestedShippingNameSet;
  }

  /** Gets the address ids that not exist in the result but
   *  were requested in the callback.
   *
   * @return Returns the missing requested result address ids.
   */
  public Set<String> getMissingRequestedAddressIdSet() {
    return missingRequestedAddressIdSet;
  }

  /** The shipping methods that not exist in the result but were
   * requested in the callback.
   *
   * @return Returns the missing requested result shipping methods.
   */
  public Set<String> getMissingRequestedShippingNameSet() {
    return missingRequestedShippingNameSet;
  }

  /** Set the validation of the result size. If the callback had one address
   * and two methods the result will have to have 3 items.
   *
   * @param theCorrectSize True if the result size is the expected size.
   *
   * @param theExpectedResultSize The expected size of the calculation result.
   *
   * @param theResultSize The size of the calculation result.
   */
  public void setCalculationResultSize(final boolean theCorrectSize,
      final int theExpectedResultSize, final int theResultSize) {
    correctSize = theCorrectSize;
    expectedResultSize = theExpectedResultSize;
    resultSize = theResultSize;
  }

  /** Adds an address that exists in the result but was not requested in the
   * callback.
   *
   * @param addressIdCollection The addresses id to add.
   */
  public void addNotRequestedAddress(
      final Collection<String> addressIdCollection) {
    notRequestedAddressIdSet.addAll(addressIdCollection);
  }

  /** Adds a shipping method name that exists in the result but was not
   * requested in the callback.
   *
   * @param shippingMethodCollection The shipping methods names.
   */
  public void addNotRequestedShipping(
      final Collection<String> shippingMethodCollection) {
    notRequestedShippingNameSet.addAll(shippingMethodCollection);
  }

  /** Adds an address that not exists in the result but were requested in the
   * callback.
   *
   * @param addressIdCollection The missing addresses id to add.
   */
  public void addMissingRequestedAddress(
      final Collection<String> addressIdCollection) {
    missingRequestedAddressIdSet.addAll(addressIdCollection);
  }

  /** Adds a shipping method name that not exists in the result but were
   * requested in the callback.
   *
   * @param shippingMethodCollection The missing shipping methods names.
   */
  public void addMissingRequestedShipping(
      final Collection<String> shippingMethodCollection) {
    missingRequestedShippingNameSet.addAll(shippingMethodCollection);
  }

  /** True if the size of the result is incorrect, or if there was an address
   * id or shipping method in the result that was not requested in the
   * callback.
   *
   * @return Returns if the validation has errors.
   */
  public boolean isCalculationFailed() {
    boolean error = false;
    if (!correctSize
        || notRequestedAddressIdSet.size() > 0
        || notRequestedShippingNameSet.size() > 0
        || missingRequestedAddressIdSet.size() > 0
        || missingRequestedShippingNameSet.size() > 0
        || calculationTimeout) {
      error = true;
    }
    return error;
  }

  /** Sets the calculation as failed.
   * Use when the calculation response time is higher than the expected.
   */
  public void markCalculationAsTimeout() {
    calculationTimeout = true;
  }

  /** True if the calculation failed.
   *
   * @return Returns if the calculation
   * response time is higher than the expected.
   */
   public boolean isCalculationTimeout() {
    return calculationTimeout;
  }

  /** Gets the merchant calculation validation description.
   *
   * @return Returns a string description of this validation.
   */
  public String toString() {
    StringBuffer sf = new StringBuffer();
    if (isCalculationFailed()) {
      sf.append("\n\n>>The merchant calculation failed.");
    } else {
      sf.append("\n>>The merchant calculation was successful.");
    }
    if (isCalculationTimeout()) {
      sf.append("\nThe calculation exceeded the maximum response time ( ");
      sf.append(maxResponseTime);
      sf.append(" milliseconds).");
    }
    sf.append("\nThe calculation callback ");
    if (!correctSize) {
      sf.append("did not ");
    }
    sf.append("received the expected amount of results.");
    sf.append("\nExcepected result size: ");
    sf.append(expectedResultSize);
    sf.append("\nActual result size: ");
    sf.append(resultSize);

    if (missingRequestedAddressIdSet.size() > 0) {
      sf.append(
          "\nThere are missing responses for address ids"
          + " that were requested in the callback: ");
      for (String id : missingRequestedAddressIdSet) {
        sf.append(id);
      }
    }
    if (missingRequestedShippingNameSet.size() > 0) {
      sf.append(
          "\nThere are missing responses for shipping method names"
          + " that were requested in the callback: ");
      for (String name : missingRequestedShippingNameSet) {
        sf.append(name);
      }
    }
    if (notRequestedAddressIdSet.size() > 0) {
      sf.append(
          "\nThere are responses for address ids"
          + " that were not requested in the callback: ");
      for (String id : notRequestedAddressIdSet) {
        sf.append(id);
      }
    }
    if (notRequestedShippingNameSet.size() > 0) {
      sf.append(
          "\nThere are responses for shipping method names"
          + " that were not requested in the callback: ");
      for (String name : notRequestedShippingNameSet) {
        sf.append(name);
      }
    }
    return sf.toString();
  }
}
